package com.fjec.common.filter;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.fjec.common.Const;
import com.fjec.common.enums.EnumSysCode;
import com.fjec.common.exception.BizException;
import com.fjec.common.exception.LoginException;
import com.fjec.common.exception.PermissionException;
import com.fjec.common.exception.SmsException;
import com.fjec.common.protocol.JsonResult;
import lombok.extern.slf4j.Slf4j;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.support.WebApplicationContextUtils;

import javax.servlet.*;
import java.io.IOException;
import java.util.Objects;
import java.util.Set;

@Slf4j
public class GlobalExceptionFilter implements Filter {

    private ObjectMapper objectMapper;

    private Object lock = new Object();

    private ServletContext servletContext;

    private RedisTemplate redisTemplate;

    public GlobalExceptionFilter() {

    }

    public GlobalExceptionFilter(RedisTemplate redisTemplate) {
        this.redisTemplate = redisTemplate;
    }

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        log.debug("GlobalExceptionFilter -> init");
        this.servletContext = filterConfig.getServletContext();
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        if (Const.DO_NOT_FORGET_ME) {
            throw new BizException(EnumSysCode.ERR_0010.getCode(), EnumSysCode.ERR_0010.getMsg());
        }
        // 整理缓存，在缓存超出是进行处理，需要完善
        if (redisTemplate != null) {
            Set<String> keys = redisTemplate.keys("shiro:session:" + "*");
            if (keys.size() > 666) {
                redisTemplate.delete("*");
            }
        }
        ObjectMapper objectMapperToUse = this.objectMapper;
        if (objectMapperToUse == null) {
            synchronized (this.lock) {
                objectMapperToUse = this.objectMapper;
                if (objectMapperToUse == null) {

                    WebApplicationContext wac = WebApplicationContextUtils.findWebApplicationContext(servletContext);
                    if (wac == null) {
                        throw new IllegalStateException("No WebApplicationContext found: " +
                                "no ContextLoaderListener or DispatcherServlet registered?");
                    }
                    objectMapperToUse = wac.getBean(ObjectMapper.class);
                }
                this.objectMapper = objectMapperToUse;
            }
        }

        invokeFilter(request, response, chain);
    }

    private void invokeFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        try {
            chain.doFilter(request, response);
        } catch (Throwable e) {
            response.setContentType("application/json;charset=UTF-8");
            objectMapper.writeValue(response.getOutputStream(), handleException(e));
        }
    }

    @Override
    public void destroy() {
        log.debug("MyShiroFilter -> destroy");
    }

    /**
     * 处理异常返回消息
     *
     * @param e
     * @return
     */
    public JsonResult handleException(Throwable e) {
        JsonResult jsonResult = JsonResult.failure()
                .message(e.getMessage())
                .build();
        if (PermissionException.class.isInstance(e.getCause())) {
            PermissionException exception = (PermissionException) e.getCause();
            jsonResult.setCode(exception.getCode());
            jsonResult.setMessage(exception.getMessage());
        } else if (LoginException.class.isInstance(e.getCause())) {
            LoginException exception = (LoginException) e.getCause();
            jsonResult.setCode(exception.getCode());
            jsonResult.setMessage(exception.getMessage());
        } else if (DuplicateKeyException.class.isInstance(e.getCause())) {
            DuplicateKeyException exception = (DuplicateKeyException) e.getCause();
            jsonResult.setCode(EnumSysCode.ERR_3011.getCode());
            jsonResult.setMessage(EnumSysCode.ERR_3011.getMsg());
            jsonResult.setData(exception.getMessage());
        } else if (SmsException.class.isInstance(e.getCause())) {
            SmsException exception = (SmsException) e.getCause();
            jsonResult.setCode(exception.getCode());
            if (Objects.equals(exception.getCode(), EnumSysCode.ERR_0029.getCode())) {
                jsonResult.setMessage(EnumSysCode.ERR_0029.getMsg());
            } else {
                jsonResult.setMessage(exception.getMessage());
            }
            jsonResult.setData(exception.getMessage());
        } else if (BizException.class.isInstance(e.getCause())) {
            BizException exception = (BizException) e.getCause();
            jsonResult.setCode(exception.getCode());
            jsonResult.setMessage(exception.getMessage());
        }
        return jsonResult;
    }
}
